<?php
// +---------------------------------------------------------------------+
// | OneBase    | [ WE CAN DO IT JUST THINK ]                            |
// +---------------------------------------------------------------------+
// | Licensed   | http://www.apache.org/licenses/LICENSE-2.0 )           |
// +---------------------------------------------------------------------+
// | Author     | Bigotry <3162875@qq.com>                               |
// +---------------------------------------------------------------------+
// | Repository | https://gitee.com/Bigotry/OneBase                      |
// +---------------------------------------------------------------------+

namespace app\common\behavior;

use think\facade\Db;
// 替换原有 Cache 引用为门面，并引入 Config 门面
use think\facade\Config;
use think\facade\Cache;

/**
 * 初始化基础信息行为
 */
class InitBase
{

    // 增加初始化一次性保护，避免重复执行导致常量重复定义
    private static $initialized = false;

    /**
     * 初始化行为入口
     */
    public function run()
    {
        // 一次性保护：若已初始化则直接返回
        if (self::$initialized) {
            return;
        }
        self::$initialized = true;

        // 移除安装重定向，统一交由 public.php 入口检测
        $this->redirectToInstallIfDbConfigMissing();

        // 初始化常量
        $this->initConst();
        
        // 初始化配置
        $this->initConfig();
        
        // 新增：初始化动态配置常量（含 DB_LIST_ROWS、API_KEY、JWT_KEY、STATIC_DOMAIN）
        $this->initTmconfig();
        
        // 注册命名空间
        $this->registerNamespace();
        
        // 初始化自动缓存
        Config::get('is_auto_cache', false) && $this->initAutoCache();
    }
    
    /**
     * 初始化常量
     */
    private function initConst()
    {
        
        // 初始化分层名称常量
        $this->initLayerConst();
        
        // 初始化结果常量
        $this->initResultConst();
        
        // 初始化数据状态常量
        $this->initDataStatusConst();
        
        // 初始化时间常量
        $this->initTimeConst();
        
        // 初始化系统常量
        $this->initSystemConst();
        
        // 初始化路径常量
        $this->initPathConst();
        
        // 初始化API常量
        $this->initApiConst();
    }
    
    /**
     * 初始化分层名称常量
     */
    private function initLayerConst()
    {
        
        define('LAYER_LOGIC_NAME'       , 'logic');
        define('LAYER_MODEL_NAME'       , 'model');
        define('LAYER_SERVICE_NAME'     , 'service');
        define('LAYER_CONTROLLER_NAME'  , 'controller');
        define('LAYER_VALIDATE_NAME'    , 'validate');
        define('LAYER_VIEW_NAME'        , 'view');
    }
    
    /**
     * 初始化结果标识常量
     */
    private function initResultConst()
    {
        
        define('RESULT_SUCCESS'         , 'success');
        define('RESULT_ERROR'           , 'error');
        define('RESULT_REDIRECT'        , 'redirect');
        define('RESULT_MESSAGE'         , 'message');
        define('RESULT_URL'             , 'url');
        define('RESULT_DATA'            , 'data');
    }
    
    /**
     * 初始化数据状态常量
     */
    private function initDataStatusConst()
    {
        
        define('DATA_STATUS_NAME'       , 'status');
        define('DATA_NORMAL'            , 1);
        define('DATA_DISABLE'           , 0);
        define('DATA_DELETE'            , -1);
        define('DATA_SUCCESS'           , 1);
        define('DATA_ERROR'             , 0);
    }
    
    /**
     * 初始化API常量
     */
    private function initApiConst()
    {
        
        define('API_CODE_NAME'          , 'code');
        define('API_MSG_NAME'           , 'msg');
    }
    
    /**
     * 初始化时间常量
     */
    private function initTimeConst()
    {
        
        define('TIME_CT_NAME'           , 'create_time');
        define('TIME_UT_NAME'           , 'update_time');
        define('TIME_NOW'               , time());
    }
    
    /**
     * 初始化路径常量
     */
    private function initPathConst()
    {
        // TP8 无 ROOT_PATH 常量，这里通过 App 获取并在首次初始化时定义
        $rootPath = defined('ROOT_PATH')
            ? ROOT_PATH
            : (function_exists('\app') ? \app()->getRootPath() : realpath(dirname(__DIR__, 3)) . DIRECTORY_SEPARATOR);

        if (!defined('ROOT_PATH')) {
            define('ROOT_PATH', $rootPath);
        }

        define('PATH_ADDON'             , ROOT_PATH   . SYS_ADDON_DIR_NAME . DS);
        define('PATH_PUBLIC'            , ROOT_PATH   . 'public'    . DS);
        define('PATH_UPLOAD'            , PATH_PUBLIC . 'upload'    . DS);
        define('PATH_PICTURE'           , PATH_UPLOAD . 'picture'   . DS);
        define('PATH_FILE'              , PATH_UPLOAD . 'file'      . DS);
        define('PATH_SERVICE'           , ROOT_PATH   . DS . SYS_APP_NAMESPACE . DS . SYS_COMMON_DIR_NAME . DS . LAYER_SERVICE_NAME . DS);
        define('PATH_COMMON_LOGIC'      , SYS_APP_NAMESPACE . SYS_DS_CONS . SYS_COMMON_DIR_NAME . SYS_DS_CONS . LAYER_LOGIC_NAME . SYS_DS_CONS);
    }
    
    /**
     * 初始化系统常量
     */
    private function initSystemConst()
    {

        // 兼容新配置，app_namespace 为空则回退为 'app'
        $appNamespace = Config::get('app_namespace', 'app');
        // 防御性处理，避免取到数组或空值导致后续路径拼接触发“Array to string conversion”
        if (!is_string($appNamespace) || $appNamespace === '') {
            $appNamespace = 'app';
        }

        define('SYS_APP_NAMESPACE'      , $appNamespace);
        define('SYS_HOOK_DIR_NAME'      , 'hook');
        define('SYS_ADDON_DIR_NAME'     , 'addon');
        define('SYS_DRIVER_DIR_NAME'    , 'driver');
        define('SYS_COMMON_DIR_NAME'    , 'common');
        define('SYS_STATIC_DIR_NAME'    , 'static');
        define('SYS_VERSION'            , '1.3.6');
        define('SYS_ADMINISTRATOR_ID'   , 1);
        define('SYS_DS_PROS'            , '/');
        define('SYS_DS_CONS'            , '\\');

        if (!defined('DS')) {
            define('DS', DIRECTORY_SEPARATOR);
        }

        if (!defined('EXT')) {
            define('EXT', '.php');
        }

        $database_config                = Config::get('database', []);
        
        define('SYS_DB_PREFIX'          , $database_config['connections']['mysql']['prefix'] ?? '');
        define('SYS_ENCRYPT_KEY'        , $database_config['sys_data_key'] ?? 'OneBase');
    }
    
    /**
     * 初始化配置信息
     */
    private function initConfig()
    {
        try {
            // 使用模型 + auto_cache 恢复原版逻辑（TP8 适配：all -> select）
            $model = model(SYS_COMMON_DIR_NAME . SYS_DS_PROS . 'Config');
    
            // 缓存配置列表，避免每次初始化都访问数据库
            \think\facade\Log::info("InitBase::initConfig: about to auto_cache 'config_list'");
            $config_list = auto_cache('config_list', create_closure($model, 'select'));
            \think\facade\Log::info("InitBase::initConfig: auto_cache 'config_list' done, type=" . (is_object($config_list) ? get_class($config_list) : gettype($config_list)));
    
            // 兼容 Collection/数组，统一转为数组后注入全局配置
            $config_array = [];
            if (!empty($config_list)) {
                foreach ($config_list as $info) {
                    $row = is_array($info) ? $info : (method_exists($info, 'toArray') ? $info->toArray() : []);
                    if (empty($row) || empty($row['name'])) {
                        continue;
                    }
                    $config_array[$row['name']] = $row['value'] ?? '';
                }
            }
    
            if (!empty($config_array)) {
                // 批量注入到全局配置，供 parse_config_array/parse_config_attr 使用
                config($config_array);
                \think\facade\Log::info("InitBase::initConfig: config injected count=" . count($config_array));
            }
        } catch (\Throwable $e) {
            // 不再静默，完整记录异常信息
            \think\facade\Log::error("InitBase::initConfig: exception: " . $e->getMessage() . "\n" . $e->getTraceAsString());
            // 这里仍然不向上抛（避免影响页面打开），但日志里会有完整的信息，便于你定位到具体根因
        }
        // 注意：run() 中已调用 initTmconfig()，此处不重复调用，避免常量重复定义
    }
    
    /**
     * 当数据库配置缺失或不完整时，跳转到安装程序
     */
    private function redirectToInstallIfDbConfigMissing(): void
    {
        // 安装模块自身与 CLI 环境不参与跳转
        if ((defined('BIND_MODULE') && BIND_MODULE === 'install') || PHP_SAPI === 'cli') {
            return;
        }

        // 读取数据库配置
        $cfg = Config::get('database');
        if (!is_array($cfg)) {
            $this->goInstall();
            return;
        }

        // 检查默认连接与具体连接项
        $default     = $cfg['default'] ?? 'mysql';
        $connections = $cfg['connections'] ?? [];
        $conn        = $connections[$default] ?? null;

        // 核心字段完整性校验
        $dbName   = $conn['database']   ?? '';
        $hostname = $conn['hostname']   ?? '';
        $username = $conn['username']   ?? '';
        // 可选：使用 sys_data_key 判定安装完成（不为默认值 OneBase）
        $sysKey   = $cfg['sys_data_key'] ?? 'OneBase';

        $incomplete = (empty($conn) || $dbName === '' || $hostname === '' || $username === '');
        $notInstalled = ($sysKey === 'OneBase');

        if ($incomplete || $notInstalled) {
            $this->goInstall();
        }
    }

    /**
     * 跳转到安装程序
     */
    private function goInstall(): void
    {
        // 使用相对路径 /install，router.php 已支持该入口
        header('Location: /install', true, 302);
        exit;
    }
    
    /**
     * 初始化动态配置信息
     */
    private function initTmconfig()
    {
        
        // 使用助手 config() 读取文本型配置，绕过 Config::pull() 的 array 返回类型约束
        $list_rows     = config('list_rows', null, 10);
        $api_key       = config('api_key', null, 'OneBase');
        $jwt_key       = config('jwt_key', null, 'OneBase');
        $static_domain = config('static_domain', null, null);
    
        // 规范化类型
        $list_rows = is_numeric($list_rows) ? (int)$list_rows : 10;
    
        define('DB_LIST_ROWS'  , $list_rows);
        define('API_KEY'       , (string)$api_key);
        define('JWT_KEY'       , (string)$jwt_key);
        define('STATIC_DOMAIN' , empty($static_domain) ? null : (string)$static_domain);
    }
    
    /**
     * 注册命名空间
     */
    private function registerNamespace()
    {
        
        // 使用 Composer 动态注册插件根命名空间，兼容 TP8 无 Loader 的场景
        // 若未定义 PATH_ADDON 则直接返回
        if (!defined('PATH_ADDON')) {
            return;
        }

        $composerLoader = null;
        foreach (spl_autoload_functions() as $func) {
            if (is_array($func) && $func[0] instanceof \Composer\Autoload\ClassLoader) {
                $composerLoader = $func[0];
                break;
            }
        }

        if ($composerLoader) {
            // 将 "addon\" 命名空间映射到插件目录
            $composerLoader->addPsr4(SYS_ADDON_DIR_NAME . '\\', PATH_ADDON);
        }
    }
    
    /**
     * 初始化自动缓存
     */
    private function initAutoCache()
    {
        
        $ob_auto_cache          = Cache::get('ob_auto_cache');
        $ob_auto_cache_keys     = Cache::get('ob_auto_cache_keys');
        
        !false === $ob_auto_cache_keys ?: Cache::set('ob_auto_cache_keys', []);
        
        if (empty($ob_auto_cache)) {
            
            $list  = \think\facade\Db::query('SHOW TABLE STATUS');
            
            $ob_auto_cache = [];
            
            foreach ($list as $v) {

                $ob_auto_cache[$v['Name']]['name']          = $v['Name'];
                $ob_auto_cache[$v['Name']]['version']       = DATA_NORMAL;
                $ob_auto_cache[$v['Name']]['data_version']  = DATA_NORMAL;
            }
            
            Cache::set('ob_auto_cache', $ob_auto_cache);
        }
        
        $this->autoClearCache($ob_auto_cache_keys);
    }
    
    /**
     * 自动清理缓存
     */
    private function autoClearCache($keys = [])
    {
    
        // 检查自动缓存是否过期
        if (!empty($keys)) {

            foreach ($keys as $k => $v) {

                if (!Cache::has($v)) {

                    Cache::delete($v);

                    unset($keys[$k]);
                }
            }

            Cache::set('ob_auto_cache_keys', array_values($keys));
        }

        // 兼容新旧缓存配置结构
        $cache_config = Config::get('cache', []);
        $path = $cache_config['path'] ?? Config::get('cache.stores.file.path', '');

        if (!empty($path) && function_exists('rm_empty_dir')) {
            rm_empty_dir($path);
        }
    }
    
    // 在事件系统中作为监听器使用，统一调用 run
    public function handle()
    {
        $this->run();
    }
}
